import {
    _decorator,
    color,
    Component,
    EventTouch,
    gfx,
    js,
    Node,
    Sprite,
    UITransform,
    v2,
    v3,
    Vec2,
} from 'cc';
const { ccclass, property } = _decorator;

const spriteBufferMap: WeakMap<Sprite, Uint8Array> = new WeakMap();

const _pos = v3();
const _pos2 = v3();
@ccclass('PixelClick')
export class PixelClick extends Component {
    @property(Sprite)
    sprite: Sprite = null!;

    protected onLoad(): void {
        this.node.on(Node.EventType.TOUCH_START, this.touchEvent, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this.touchEvent, this);

        this.enablePixelHitTest();
    }

    touchEvent(e: EventTouch) {
        const ui_pos = e.getUILocation();
        this.testGetColor(ui_pos);
    }

    testGetColor(ui_pos: Vec2) {
        const sprite = this.node.getComponent(Sprite);
        const ui = this.node.getComponent(UITransform);
        ui.convertToNodeSpaceAR(v3(ui_pos.x, ui_pos.y, 0), _pos2);
        const index = _getBufferIndex(v2(_pos2.x, _pos2.y), sprite);

        const col = _checkPixels(v2(_pos2.x, _pos2.y), sprite);
        console.log(col);
        if (this.sprite) {
            this.sprite.color = col;
        }
    }

    start() {
        const sprite = this.node.getComponent(Sprite);
        if (!sprite) {
            return;
        }
        const buffer = readPixelsFromSprite(sprite);
        console.log(buffer);
    }

    enablePixelHitTest() {
        if (!this.enabledInHierarchy) {
            return;
        }
        const sprite = this.node.getComponent(Sprite);
        if (!sprite) {
            return;
        }

        // const uiTransform = this.node.getComponent(UITransform)!;
        // console.log(uiTransform.hitTest);
    }

    private enableSpriteFrameListener(sprite: Sprite) {
        if (!sprite) {
            return;
        }
        let _property = 'spriteFrame';
        let desc = js.getPropertyDescriptor(sprite, _property);
        if (!!desc.set) {
            Object.defineProperty(sprite, _property, {
                get: desc.get,
                set: (value) => {
                    if (sprite.spriteFrame != value) {
                        spriteBufferMap.delete(sprite);
                    }
                    desc.set.call(sprite, value);
                },
            });
            return;
        }
    }
}

// 检查像素
function _checkPixels(position: Vec2, sprite: Sprite) {
    let buffer = readPixelsFromSprite(sprite);
    let index = _getBufferIndex(position, sprite);

    return color(
        buffer[index + 0],
        buffer[index + 1],
        buffer[index + 2],
        buffer[index + 3],
    );
}

/**
 * 读取Sprite像素
 * 返回 buffer对象 , buffer对象格式
 * buffer[i*4+0] r
 * buffer[i*4+1] g
 * buffer[i*4+2] b
 * buffer[i*4+3] a
 *
 */
function readPixelsFromSprite(sprite: Sprite) {
    let buffer: Uint8Array = null;

    let spriteFrame = sprite.spriteFrame;
    let texture = spriteFrame.texture;
    let tx = spriteFrame.rect.x;
    let ty = spriteFrame.rect.y;
    if (spriteFrame.packable && spriteFrame.original) {
        texture = spriteFrame.original._texture;
        tx = spriteFrame.original._x;
        ty = spriteFrame.original._y;
    }
    let width = spriteFrame.rect.width;
    let height = spriteFrame.rect.height;

    let gfxTexture = texture.getGFXTexture();
    let gfxDevice = texture['_getGFXDevice']();
    let bufferViews = [];
    let region = new gfx.BufferTextureCopy();
    buffer = new Uint8Array(width * height * 4);
    (region.texOffset.x = tx), (region.texOffset.y = ty);
    region.texExtent.width = width;
    region.texExtent.height = height;
    bufferViews.push(buffer);
    gfxDevice?.copyTextureToBuffers(gfxTexture, bufferViews, [region]);
    return buffer;
}

function _getBufferIndex(position: Vec2, sprite: Sprite) {
    const spriteFrame = sprite.spriteFrame;
    const texWidth = spriteFrame.rect.width;
    const texHeight = spriteFrame.rect.height;
    const originSize = spriteFrame.originalSize;
    const uiTrans = sprite.getComponent(UITransform)!;

    const anchorX = uiTrans.anchorX;
    const anchorY = uiTrans.anchorY;

    const contentWidth = uiTrans.width;
    const contentHeight = uiTrans.height;

    let index = -1;

    if (sprite.trim) {
        let x = Math.floor(
            position.x / (contentWidth / texWidth) + texWidth * anchorX,
        );
        let y = Math.floor(
            texHeight -
                (position.y / (contentHeight / texHeight) +
                    texHeight * anchorY),
        );
        index = (y * texWidth + x) * 4;
    } else {
        let scaleX = contentWidth / originSize.width; // 计算原始图像与节点大小的缩放系数
        let scaleY = contentHeight / originSize.height;

        let leftPoint = position.x + contentWidth * anchorX; // 转换到左上角坐标
        let topPoint = Math.abs(
            position.y + contentHeight * (anchorY - 1),
        );

        let tx = spriteFrame.rect.x;
        let ty = spriteFrame.rect.y;
        if (spriteFrame.packable && spriteFrame.original) {
            tx = spriteFrame.original._x;
            ty = spriteFrame.original._y;
        }
        // 计算鼠标在图像像素上的位置
        let x = Math.floor((leftPoint - tx * scaleX) / scaleX);
        let y = Math.floor((topPoint - ty * scaleY) / scaleY);
        index = (y * texWidth + x) * 4;
    }

    return index;
}
